// #ifndef _POLL_SVR_H_
// #define _POLL_SVR_H_
// #include <iostream>
// #include <sys/select.h>
// #include <string>
// #include <sys/time.h>
// #include <vector>

// // #include "Log.hpp"
// #include "Sock.hpp"
// using namespace std;

// #define Num (sizeof(fd_set) * 8)
// #define FD_NONE -1

// // select 目前只完成读取，写入和异常不做处理 --epoll(写完整版)
// class SelectServer
// {
// public:
//     SelectServer(const uint16_t &port = 8080) : _port(port) // 初始化select服务器
//     {
//         _listensock = Sock::Socket();
//         Sock::Bind(_listensock, _port);
//         Sock::Listen(_listensock);
//         ClearyFd(); // 清空文件描述符表
//         fd_array[0] = _listensock;
//     }
//     void Start() // 开始用select监视一批套接字/文件描述符
//     {
//         while (true) // 不断监视一批套接字
//         {
//             uint16_t port;
//             string ip;
//             fd_set readfds; // 若i号文件描述符就绪，则readfds的第i个比特位就为真
//             FD_ZERO(&readfds);

//             DebugPrint(); // 检查fd_array当前有哪些文件描述符

//             // 1.accept，用 单路的方法&&阻塞等待 对方的连接到来，再获取对方链接上来。(accpet当成了input,链接到来当成了数据就绪)
//             //  int serversock=Sock::Accept(_listensock,&ip,&port);

//             // 2.select，用多路转接的方法 多路&&(阻塞等待 or 非阻塞等待 or timeout) 来批量等待对方链接到来，再获取链接上来。
//             //(select当成了input,链接到阿里当成了数据就绪)
//             // select充当的是一个数据监视器，监视每个文件描述符是否就绪（是否有数据到来-监听套接字：是否有链接到来，服务套接字：是否有数据到来）

//             int maxfd = _listensock;
//             for (int i = 0; i < Num; i++)
//             {
//                 if (fd_array[i] == FD_NONE)
//                     continue;
//                 FD_SET(fd_array[i], &readfds); // 设置关心的文件描述符
//                 if (fd_array[i] > maxfd)
//                     maxfd = fd_array[i];
//             }

//             struct timeval timeout = {0, 0};

//             // 1. nfds: 随着我们获取的sock越来越多，随着我们添加到select的sock越来越多，注定了nfds每一次都可能要变化,我们需要对它动态计算
//             // 2. rfds/writefds/exceptfds：都是输入输出型参数，输入输出不一定以一样的，所以注定了我们每一次都要对rfds进行重新添加
//             // 3. timeout: 都是输入输出型参数，每一次都要进行重置，前提是你要的话
//             // 1,2 => 注定了我们必须自己将合法的文件描述符需要单独全部保存起来 用来支持：1. 更新最大fd 2.更新位图结构
//             int n = select(maxfd + 1, &readfds, nullptr, nullptr, nullptr); // 阻塞式等待
//             // timeval参数 为nullptr时阻塞等待 ，为{0,0}时非阻塞等待 , 为{x,y}时按等待x s.y ms式等待
//             switch (n) // 获取的文件描述符的数目
//             {
//             case 0:
//                 logMessage(DEBUG, "time out...");
//                 break;
//             case -1:
//                 logMessage(WARNING, "no event...");
//                 break;
//             default:
//                 logMessage(DEBUG, "get a event...");
//                 HandlerEvent(readfds); // 处理事件
//                 break;
//             }
//         }
//     }
//     ~SelectServer() {}

// private:
//     void ClearyFd()
//     {
//         for (int i = 0; i < Num; i++)
//         {
//             fd_array[i] = FD_NONE;
//         }
//     }
//     void HandlerEvent(const fd_set &readfds)
//     {
//         for (int i = 0; i < Num; i++)
//         {
//             if (fd_array[i] == FD_NONE)
//                 continue;

//             if (FD_ISSET(fd_array[i], &readfds)) // 有文件描述符就绪
//             {
//                 if (fd_array[i] == _listensock)
//                     Accepter(); // 监听套接字文件描述符就绪
//                 else
//                     Recever(i); // 服务套接字文件描述符就绪
//             }
//         }
//     }

//     void Accepter() // 获取链接，并发配服务套接字，并把新的服务套接字放入fd_array中，一个服务套接字对应一个链接。listensock一次只获取一个链接
//     {
//         string clientip;
//         uint16_t clientport = 0;

//         int sock = Sock::Accept(_listensock, &clientip, &clientport);
//         if (sock < 0)
//         {
//             logMessage(ERROR, "accept error");
//             return;
//         }
//         logMessage(DEBUG, "accept success,[%s:%d]", clientip.c_str(), clientport);
//         // 获取上来链接之后，分配好了服务端套接字sock。但当前还不能做read和recv。因为这里是主进程，如果read和recv IO的话会有阻塞的可能。

//         // 当前派发的服务套接字sock先放入数组中,让select去监视数据的到来，当数据到来之后再调用服务套接字去获取数据，此时就不会阻塞了。
//         // 先将服务套接字sock放进fd_array数组

//         int i = 1; // 找空位置放服务套接字sock
//         for (i = 1; i < Num; i++)
//         {
//             if (fd_array[i] == FD_NONE)
//                 break;
//         }
//         if (i == Num)
//         {
//             logMessage(WARNING, "fd_array is full,close %d", sock);
//             close(sock);
//             return;
//         }
//         cout << "debug" << endl;
//         fd_array[i] = sock;
//     }

//     void Recever(int pos) // 获取(接收)数据,由服务套接字sock完成
//     {
//         // 进行数据的接收

//         logMessage(NORMAL, "data is coming...,get IO event[sock:%d]",fd_array[pos]);

//         char buffer[1024];
//         // recvfrom关系发来的数据，及发来的port、ip、family.而recv只关心发来的数据，因为accept已经获得过了port,ip等信息了
//         int n = recv(fd_array[pos], buffer, sizeof(buffer) - 1, 0); // 最后一个位置用来放'\0'
//         // 暂时先不做封装, 此时select已经帮我们进行了事件检测，fd上的数据一定是就绪的，即 本次 不会被阻塞
//         // 这样读取有bug吗？有的，你怎么保证以读到了一个完整包文呢？epoll时解决


//         if (n > 0)
//         {
//             buffer[n - 1] = '\0';
//             logMessage(DEBUG, "client[sock:%d]# %s", fd_array[pos], buffer);
//         }
//         else if (n == 0)
//         {
//             logMessage(ERROR, "client quit...,so serversock is quit,too");
//             // 1. 我们也要关闭不需要的fd
//             close(fd_array[pos]);
//             // 2. 不要让select帮我关心当前的fd了
//             fd_array[pos] = FD_NONE;
//         }
//         else
//         {
//             logMessage(WARNING, "%d serversock recv error, %d : %s", fd_array[pos], errno, strerror(errno));
//             // 1. 我们也要关闭不需要的fd
//             close(fd_array[pos]);
//             // 2. 不要让select帮我关心当前的fd了
//             fd_array[pos] = FD_NONE;
//         }
//     }

//     void DebugPrint()
//     {
//         cout << "fd_array: ";
//         for (int i = 0; i < Num; i++)
//         {
//             if (fd_array[i] == FD_NONE)
//                 continue;
//             cout << fd_array[i] << ' ';
//         }
//         cout << endl;
//     }

// private:
//     uint16_t _port;
//     int _listensock;
//     int fd_array[Num];//用来存放套接字，数组元素的值就是文件描述符的值，第几号文件描述符。用来存放3~(1024+3)的文件描述符，
//                       // 因为fd_set只有1024个位，所以只能标记1024个套接字，所以我们这也就只需要1024个大小的数组
// };

// #endif





#ifndef POLL_SVR_H_
#define POLL_SVR_H_

#include <iostream>
#include <string>
#include <vector>
#include <poll.h>
#include <sys/time.h>
#include <cstdlib>

#include "Sock.hpp"
using namespace std;

#define FD_NONE -1
// #define fds_len 100


class PollServer
{
public:
    static const int fds_nums = 100;
public:
    PollServer(const uint16_t& port=8080):_port(port),_fds_nums(fds_nums)
    {
        _listensock=Sock::Socket();
        Sock::Bind(_listensock,_port);
        Sock::Listen(_listensock);
        _fds = new struct pollfd[_fds_nums]; 
        ClearyFd(FD_NONE);
        _fds[0].fd=_listensock;
        _fds[0].events=POLLIN;
        _timeout=1000;//ms
        logMessage(NORMAL,"Init poll server success,....");
        DebugPrint();
        
    }
    void Start()
    {
        for(;;)
        {
            cout<<"======================"<<endl;
            DebugPrint();
            int n=poll(_fds,_fds_nums,_timeout);
            if(n==0)
            {
                logMessage(WARNING,"time out ...");
            }
            else if(n<0)
            {
                logMessage(ERROR,"poll server error");
            }
            else 
            {
                logMessage(NORMAL,"get some event");
                HandlerEvent();
            }
            cout<<"======================"<<endl;
            cout<<endl;
            
        }
    }
    ~PollServer()
    {
        if(_fds)delete[] _fds;
    }
private:

    void HandlerEvent()
    {
        for(int i=0;i<_fds_nums;i++)
        {
            if(_fds[i].fd==FD_NONE)continue;
            if(_fds[i].revents&POLLIN)//事件就绪
            {
                if(_fds[i].fd==_listensock)Accepter();
                else Recever(i);
            }
        }
        
    }

    void Accepter()//获取链接上来并放入_fds中
    {
        uint16_t port;
        string ip;
        int sock=Sock::Accept(_listensock,&ip,&port);

        logMessage(DEBUG,"get a link [%s:%d],serversock:%d",ip.c_str(),port,sock);
        
        int i=_listensock;
        for(;i<_fds_nums;i++)
        {
            if(_fds[i].fd==FD_NONE)break;
        }
        if(i==_fds_nums)
        {
            //1.扩容方案
            logMessage(DEBUG,"poll server is full,now poll server would reserve...");
            int new_fds_nums=_fds_nums * 2;
            _fds=(struct pollfd*)realloc(_fds,sizeof(struct pollfd)*new_fds_nums);
            _fds_nums=new_fds_nums;
            logMessage(DEBUG,"poll server reserve done...");
            // //2.不扩容方案
            // logMessage(DEBUG,"poll server is full...,close sock:%d",sock);
            // close(sock);
            // return ;
        }
        _fds[i].fd=sock;
        _fds[i].events=POLLIN;
    }


//  void Recever(int pos)
//     {
//         // 读事件就绪：INPUT事件到来、recv，read
//         logMessage(DEBUG, "message in, get IO event: %d", _fds[pos]);
//         // 暂时先不做封装, 此时select已经帮我们进行了事件检测，fd上的数据一定是就绪的，即 本次 不会被阻塞
//         // 这样读取有bug吗？有的，你怎么保证以读到了一个完整包文呢？
//         char buffer[1024];
//         int n = recv(_fds[pos].fd, buffer, sizeof(buffer)-1, 0);
//         if(n > 0){
//             buffer[n] = 0;
//             logMessage(DEBUG, "client[%d]# %s", _fds[pos].fd, buffer);
//         }
//         else if(n == 0){
//             logMessage(DEBUG, "client[%d] quit, me too...", _fds[pos].fd);
//             // 1. 我们也要关闭不需要的fd
//             close(_fds[pos].fd);
//             // 2. 不要让select帮我关心当前的fd了
//             _fds[pos].fd = FD_NONE;
//             _fds[pos].events = 0;
//         }
//         else{
//             logMessage(WARNING, "%d sock recv error, %d : %s", _fds[pos].fd, errno, strerror(errno));
//             // 1. 我们也要关闭不需要的fd
//             close(_fds[pos].fd);
//             // 2. 不要让select帮我关心当前的fd了
//             _fds[pos].fd = FD_NONE;
//             _fds[pos].events = 0;
//         }
//     }

    void Recever(int pos)
    {
        logMessage(DEBUG,"message is come,start get IO...");
        char buffer[1024];
        struct sockaddr_in clientinfo;
        bzero(&clientinfo, sizeof(clientinfo));
        socklen_t len=sizeof(clientinfo);
        int n=recvfrom(_fds[pos].fd,buffer,sizeof(buffer)-1,0,\
        (struct sockaddr*)&clientinfo,&len);
        // int n=recv(_fds[pos].fd,buffer,sizeof(buffer)-1,0);

        if(n==0)
        {
            logMessage(DEBUG,"client quit...,server sock %d quit ,too",_fds[pos].fd);
            close(_fds[pos].fd);
            _fds[pos].fd=FD_NONE;
            _fds[pos].events=0;
            return;
        }
        else if(n<0)
        {
            logMessage(ERROR,"serversock %d recvfrom error,serversock %d quit...",\
            _fds[pos].fd,_fds[pos].fd);
            close(_fds[pos].fd);
            _fds[pos].fd=FD_NONE;
            _fds[pos].events=0;
            return;
        }

        uint16_t port=ntohs(clientinfo.sin_port);
        string ip=inet_ntoa(clientinfo.sin_addr);

        buffer[n-1]='\0';
        
        // cout<<ip<<endl;
        logMessage(DEBUG,"[ip:%s |port:%d |sock:%d]#say:%s",ip.c_str(),port,_fds[pos],buffer);
        // logMessage(DEBUG,"[sock:%d]#say:%s",_fds[pos].fd,buffer);

        cout<<endl;
    }
private:
    void ClearyFd(int fds_icon)
    {
        for(int i=0;i<_fds_nums;i++)
        {
            _fds[i].fd=fds_icon;
            _fds[i].events=_fds[i].revents=0;
        }
    }

    void DebugPrint()
    {
        logMessage(DEBUG,"now _fds like this:");
        for(int i=0;i<_fds_nums;i++)
        {
            if(_fds[i].fd==FD_NONE)continue;
            printf("[%d:%d] ",_fds[i].fd,_fds[i].events);
        }
        cout<<endl;
    }


private:
    uint16_t _port;
    int _listensock;
    struct pollfd* _fds;
    int _fds_nums;
    int _timeout;//0: 非阻塞    -1: 阻塞     其他大于0的数: 表示等待时间

};



#endif
